package Model.check_inside_util;

import Model.DrawModel.CalPoint;

import java.util.List;

/**
 * 检查点是否在图形内部
 * @孙爱玲,赵光华,赵敏华,常璐.基于sign(x)函数的点在多边形内外判别算法及应用[J].计算机工程与科学,2017,39(04):785-790
 * @author dingN
 * @date 2022/07/11
 */
public class CheckIsInside {

    static int K = 0; // 判断所用累加值
    static List<CalPoint> surroundCalPoints;
    static CalPoint Specific;

    /**
     * 检查 某点是否在图形内部
     * @param contourCalPoints 轮廓点， 依次连线
     * @param specific      待测点
     * @return boolean
     */
    public static boolean CheckInside(
            List<CalPoint> contourCalPoints,
            CalPoint specific
    ) {
        K = 0;
        surroundCalPoints = contourCalPoints;
        Specific = specific;
        int listLength = contourCalPoints.size();
        for (int i = 0; i < listLength; i++) {

            CalPoint firstCalPoint = contourCalPoints.get(i);
            CalPoint secondCalPoint = contourCalPoints.get((i + 1) % listLength);

            double difference = calculateVectorDifference(firstCalPoint, secondCalPoint, specific);

            int signX = sign(difference);

            if (signX == 0 && whetherOnEdge(firstCalPoint, secondCalPoint, specific)) {
                return false; // 这里认为如果待测点在某边上，也同样是不在图形内
            } else {
                AddK(firstCalPoint, secondCalPoint, specific, signX);
            }

        }

        System.out.println("K:" + K);
        if (K == 0) return false; // K=0, 点在多边形外部
        return true; // k != 0, 点在多边形内部
    }

    /**
     * 检查 某点是否在图形内部，使用自定义方程
     * @param contourCalPoints 轮廓点， 依次连线
     * @param specific      待测点
     * @return boolean
     */
    public static double CheckInsideReturnK(
            List<CalPoint> contourCalPoints,
            CalPoint specific
    ) {
        K = 0;
        surroundCalPoints = contourCalPoints;
        Specific = specific;
        int listLength = contourCalPoints.size();
        for (int i = 0; i < listLength; i++) {

            CalPoint firstCalPoint = contourCalPoints.get(i);
            CalPoint secondCalPoint = contourCalPoints.get((i + 1) % listLength);

            double difference = calculateVectorDifference(firstCalPoint, secondCalPoint, specific);

            int signX = sign(difference);

            if (signX == 0 && whetherOnEdge(firstCalPoint, secondCalPoint, specific)) {
                K += surroundCalPoints.size();
                return K;
            } else {
                AddK(firstCalPoint, secondCalPoint, specific, signX);
            }

        }

        System.out.println("K:" + K);
        return K;
    }



    /**
     * 计算向量差
     *
     * @param calPointI       point i
     * @param calPointIAddOne point i + 1
     * @param specific     待测点
     * @return double
     */
    public static double calculateVectorDifference(
            CalPoint calPointI,
            CalPoint calPointIAddOne,
            CalPoint specific
    ) {
        return (calPointI.x - specific.x) * (calPointIAddOne.y - specific.y) - (calPointIAddOne.x - specific.x) * (calPointI.y - specific.y);
    }

    /**
     * 标志函数 sign(x)
     * @param x x
     * @return int
     */
    public static int sign(
            double x
    ) {
        if (x == 0)
            return 0;
        else if (x > 0)
            return 1;
        else
            return -1;
    }

    /**
     * 待测点是否在边上
     *
     * @param calPointI       point i
     * @param calPointIAddOne point i + 1
     * @param specific     待测点
     * @return boolean
     */
    public static boolean whetherOnEdge(
            CalPoint calPointI,
            CalPoint calPointIAddOne,
            CalPoint specific
    ) {
        if ( // 在边上
                specific.x <= Math.max(calPointI.x, calPointIAddOne.x) && specific.x >= Math.min(calPointI.x, calPointIAddOne.x)
                        &&
                        specific.y <= Math.max(calPointI.y, calPointIAddOne.y) && specific.y >= Math.min(calPointI.y, calPointIAddOne.y)
        ) {

            System.out.println("待测点位于边" + calPointI + " " + calPointIAddOne + "上");
            return true;
        }
        System.out.println("待测点不位于边" + calPointI + " " + calPointIAddOne + "上");
        return false;
    }

    /**
     * 待测点是否在边上
     * @param calPointI       point i
     * @param calPointIAddOne point i + 1
     * @param specific     待测点
     * @param sign         标志
     */
    public static void AddK(
            CalPoint calPointI,
            CalPoint calPointIAddOne,
            CalPoint specific,
            int sign
    ) {
        if ((calPointIAddOne.y < specific.y && specific.y < calPointI.y) || (calPointI.y < specific.y && specific.y < calPointIAddOne.y)) {
            K += sign;
        }
    }

}
